package com.changdu.seatunnel.admin.util;

import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.databind.type.MapType;

/**
 * @author wangxiaoliang 摘自网络并改造
 */
public final class JsonUtils {

	private static final ObjectMapper CUSTOMMAPPER = new ObjectMapper();

	static {
		CUSTOMMAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
	}
	
	/**
	 * Returns a thread-local instance of JSON ObjectMapper.
	 *
	 * @return ObjectMapper.
	 */
	public static ObjectMapper getObjectMapper() {
		return CUSTOMMAPPER;
	}

	/**
	 * Converts object to JSON string
	 *
	 * @param object
	 *            to be converted
	 * @return JSON representation of object
	 */
	public static String toJson(Object object) {
		try {
			return getObjectMapper().writeValueAsString(object);
		} catch (JsonProcessingException e) {
			throw new IllegalArgumentException(
					"Given Object could not be serialized to JSON. Error: " + e.getMessage());
		}
	}

	/**
	 * Allows JSON serialization with custom mapping
	 *
	 * @param object
	 *            to be serialized
	 * @param customMapper
	 *            custom mapper
	 * @return JSON representation of object
	 */
	public static String toJson(Object object, ObjectMapper customMapper) {
		if (customMapper == null) {
			throw new IllegalArgumentException("Missing custom mapper!");
		}
		try {
			return customMapper.writeValueAsString(object);
		} catch (JsonProcessingException e) {
			throw new IllegalArgumentException(
					"Given Object could not be serialized to JSON. Error: " + e.getMessage());
		}
	}

	/**
	 * Transforms String to JSON object
	 *
	 * @param json
	 *            string to transform
	 * @param valueType
	 *            class type
	 * @param <T>
	 *            class type
	 * @return deserialized object
	 */
	public static <T> T fromJson(String json, Class<T> valueType) {
		try {
			return getObjectMapper().readValue(json, valueType);
		} catch (IOException e) {
			throw new IllegalArgumentException("Given JSON could not be de-serialized. Error: " + e.getMessage());
		}
	}

	public static <T> T fromJson(InputStream in, Class<T> clazz) {
		try {
			return getObjectMapper().readValue(in, clazz);
		} catch (IOException e) {
			throw new IllegalArgumentException("Given JSON could not be de-serialized. Error: " + e.getMessage());
		}
	}
	
	/**
	 * @param json
	 *            JSON representation of object
	 * @param reference
	 *            type reference
	 * @param <T>
	 *            class type
	 * @return deserialized object
	 */
	public static <T> T fromJson(String json, TypeReference<T> reference) {
		if (reference == null) {
			throw new IllegalArgumentException("Missing type reference!");
		}
		try {
			return getObjectMapper().readValue(json, reference);
		} catch (IOException e) {
			throw new IllegalArgumentException("Given JSON could not be deserialized. Error: " + e.getMessage());
		}
	}

	/**
	 * Allows de-serialization with custom mapping
	 *
	 * @param json
	 *            JSON representation of object
	 * @param reference
	 *            type reference
	 * @param <T>
	 *            class type
	 * @param mapper
	 *            custom object mapper
	 * @return deserialized object
	 */
	public static <T> T fromJson(String json, TypeReference<T> reference, ObjectMapper mapper) {
		if (mapper == null) {
			throw new IllegalArgumentException("Missing object mapper!");
		}
		if (reference == null) {
			throw new IllegalArgumentException("Missing type reference!");
		}
		try {
			return mapper.readValue(json, reference);
		} catch (IOException e) {
			throw new IllegalArgumentException("Given JSON could not be deserialized. Error: " + e.getMessage());
		}
	}

	/**
	 * Allows de-serialization with custom mapping
	 *
	 * @param json
	 *            JSON representation of object
	 * @param valueType
	 *            class
	 * @param mapper
	 *            custom object mapper
	 * @param <T>
	 *            class type
	 * @return deserialized object
	 */
	public static <T> T fromJson(String json, Class<T> valueType, ObjectMapper mapper) {
		if (mapper == null) {
			throw new IllegalArgumentException("Missing object mapper!");
		}
		try {
			return mapper.readValue(json, valueType);
		} catch (IOException e) {
			throw new IllegalArgumentException("Given JSON could not be deserialized. Error: " + e.getMessage());
		}
	}

	/**
	 * @param object
	 *            JSON representation of object
	 * @param reference
	 *            type reference
	 * @param <T>
	 *            class type
	 * @return deserialized object
	 */
	public static <T> T convert(Object object, TypeReference<T> reference) {
		if (reference == null) {
			throw new IllegalArgumentException("Missing type reference!");
		}
		return getObjectMapper().convertValue(object, reference);
	}

	/**
	 * @param object
	 *            JSON representation of object
	 * @param valueType
	 *            type reference
	 * @param <T>
	 *            class type
	 * @return deserialized object
	 */
	public static <T> T convert(Object object, Class<T> valueType) {
		if (valueType == null) {
			throw new IllegalArgumentException("Missing class reference!");
		}
		return getObjectMapper().convertValue(object, valueType);
	}

	public static <T> List<T> fromJsonAsList(String data, Class<?> clazz) {
		try {
			CollectionType listType = getObjectMapper().getTypeFactory().constructCollectionType(List.class, clazz);
			return getObjectMapper().readValue(data, listType);
		} catch (IOException e) {
			throw new IllegalArgumentException("Given JSON: '" + data + "' could not be deserialized to List<"
					+ clazz.getSimpleName() + ">. Error: " + e.getMessage());
		}
	}

	public static <T> Set<T> fromJsonAsSet(String data, Class<?> clazz) {
		try {
			CollectionType listType = getObjectMapper().getTypeFactory().constructCollectionType(Set.class, clazz);
			return getObjectMapper().readValue(data, listType);
		} catch (IOException e) {
			throw new IllegalArgumentException("Given JSON: '" + data + "' could not be deserialized to List<"
					+ clazz.getSimpleName() + ">. Error: " + e.getMessage());
		}
	}
	
	public static <T> List<T> fromJsonArrayAsList(JsonNode jsonNode, Class<?> clazz) {
		try {
			CollectionType listType = getObjectMapper().getTypeFactory().constructCollectionType(List.class, clazz);
			return getObjectMapper().convertValue(jsonNode, listType);
		} catch (Exception e) {
			throw new IllegalArgumentException("Given JSON: '" + jsonNode + "' could not be deserialized to List<"
					+ clazz.getSimpleName() + ">. Error: " + e.getMessage());
		}
	}

	public static <T> List<T> fromJsonAsList(InputStream in, Class<?> clazz) {
		try {
			CollectionType listType = getObjectMapper().getTypeFactory().constructCollectionType(List.class, clazz);
			return getObjectMapper().readValue(in, listType);
		} catch (IOException e) {
			throw new IllegalArgumentException("Given stream JSON exception");
		}
	}

	public static <T> List<T> fromJsonAsList(String data, Class<?> clazz, ObjectMapper mapper) {
		try {
			CollectionType listType = mapper.getTypeFactory().constructCollectionType(List.class, clazz);
			return mapper.readValue(data, listType);
		} catch (IOException e) {
			throw new IllegalArgumentException("Given JSON: '" + data + "' could not be deserialized to List<"
					+ clazz.getSimpleName() + ">. Error: " + e.getMessage());
		}
	}

	public static <T, E> Map<T, E> fromJsonAsMap(String data, Class<?> keyClazz, Class<?> elementClazz) {
		try {
			MapType mapType = getObjectMapper().getTypeFactory().constructMapType(Map.class, keyClazz, elementClazz);
			return getObjectMapper().readValue(data, mapType);
		} catch (IOException e) {
			throw new IllegalArgumentException("Given JSON: '" + data + "' could not be deserialized to Map<"
					+ keyClazz.getSimpleName() + ", " + elementClazz);
		}
	}

	public static String getAsText(JsonNode node, String name) {
		if (node == null || !node.has(name)) {
			return null;
		}
		return StringUtils.trimToNull(node.get(name).asText());
	}

	public static Long getAsLong(JsonNode node, String name) {
		if (node == null || !node.has(name)) {
			return null;
		}
		return node.get(name).asLong();
	}

	public static <T> T fromNode(JsonNode node, String field, Class<T> clazz) {
		JsonNode subNode = node.get(field);
		if (subNode != null) {
			return JsonUtils.fromJson(subNode.toString(), clazz);
		}
		return null;
	}

	public static JsonNode toJsonNode(Object object) {
		try {
			String json = getObjectMapper().writeValueAsString(object);
			return getObjectMapper().readTree(json);
		} catch (IOException e) {
			throw new IllegalArgumentException(e.getMessage());
		}
	}	
	
	public static JsonNode getJsonNode(InputStream in) {
		try {
			JsonNode jsonNode = getObjectMapper().readTree(in);
			return jsonNode;
		} catch (IOException e) {
			throw new IllegalArgumentException(e.getMessage());
		}
	}

	public static JsonNode getJsonNode(byte[] bytes) {
		try {
			JsonNode jsonNode = getObjectMapper().readTree(bytes);
			return jsonNode;
		} catch (IOException e) {
			throw new IllegalArgumentException(e.getMessage());
		}
	}

	/**
	 * 拷贝字节
	 * @param bytes
	 * @param length
	 * @return
	 */
	public static JsonNode getJsonNode(byte[] bytes, int length) {
		try {
			JsonNode jsonNode = getObjectMapper().readTree(bytes, 0, length);
			return jsonNode;
		} catch (IOException e) {
			throw new IllegalArgumentException(e.getMessage());
		}
	}
	
	/**
	 * @param <T>
	 * @param bytes
	 * @param length
	 * @param clazz
	 * @return
	 */
	public static <T> T getJsonNode(byte[] bytes, int length, Class<T> clazz) {
		try {
			T value = getObjectMapper().readValue(bytes, 0, length, clazz);
			return value;
		} catch (IOException e) {
			throw new IllegalArgumentException(e.getMessage());
		}
	}
	
	public static <T> T getJsonNode(byte[] bytes, Class<T> clazz) {
		try {
			T value = getObjectMapper().readValue(bytes, clazz);
			return value;
		} catch (IOException e) {
			throw new IllegalArgumentException(e.getMessage());
		}
	}
	
	/**
	 * @param <T>
	 * @param bytes
	 * @param length
	 * @param clazz
	 * @return
	 */
	public static <T> List<T> fromJsonAsList(byte[] bytes, int length, Class<?> clazz) {
		try {
			CollectionType listType = getObjectMapper().getTypeFactory().constructCollectionType(List.class, clazz);
			return getObjectMapper().readValue(bytes, 0, length, listType);
		} catch (IOException e) {
			throw new IllegalArgumentException("Given JSON: '" + new String(bytes) + "' could not be deserialized to List<"
					+ clazz.getSimpleName() + ">. Error: " + e.getMessage());
		}
	}
	
	public static <T> List<T> fromJsonAsList(byte[] bytes, Class<?> clazz) {
		try {
			CollectionType listType = getObjectMapper().getTypeFactory().constructCollectionType(List.class, clazz);
			return getObjectMapper().readValue(bytes, listType);
		} catch (IOException e) {
			throw new IllegalArgumentException("Given JSON: '" + new String(bytes) + "' could not be deserialized to List<"
					+ clazz.getSimpleName() + ">. Error: " + e.getMessage());
		}
	}
	
	/**
	 * 添加 json 数据
	 * @param target
	 * @param key
	 * @param value
	 * @return
	 */
	public static ObjectNode addJsonNode(JsonNode target, String key, Object value) {
		ObjectNode result = (ObjectNode) getJsonNode("{}".getBytes());
		Iterator<Entry<String, JsonNode>> fields = target.fields();
		while (fields.hasNext()) {
			Entry<String, JsonNode> entry = fields.next();
			result.set(entry.getKey(), entry.getValue());
		}
		result.putPOJO(key, value);
		return result;
	}
	
	/**
	 * 添加 json 数据
	 * @param target
	 * @param key
	 * @param value
	 * @return
	 */
	public static ObjectNode addByteNode(byte[] target, String key, Object value) {
		return addJsonNode(getJsonNode(target), key, value);
	}
	
	public static ObjectNode addByteNode(byte[] target, int length, String key, Object value) {
		return addJsonNode(getJsonNode(target, length), key, value);
	}	
	
	/**
	 * 添加 json 数据
	 * @param target
	 * @param key
	 * @param value
	 * @return
	 */
	public static ObjectNode addObjectNode(Object target, String key, Object value) {
		return addJsonNode(toJsonNode(target), key, value);
	}

	/**
	 * 生成可操作的 objectNode
	 * @return
	 */
	public static ObjectNode getObjectNode() {
		return getObjectMapper().createObjectNode();
	}
	
	/**
	 * 格式化返回
	 * @param config
	 * @return
	 */
	public static String toPrettyJson(Object config) {
		try {
			return JsonUtils.getObjectMapper()
				.writerWithDefaultPrettyPrinter().writeValueAsString(config);
		} catch (JsonProcessingException e) {
			throw new IllegalArgumentException(
					"Given Object could not be serialized to JSON. Error: " + e.getMessage());
		}
	}	
	
}